package cn.topcode.unicorn.utils.http;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;

import cn.topcode.unicorn.utils.IOUtil;

import com.alibaba.fastjson.JSON;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.protocol.HTTP;

/**
 * Http客户端请求工具
 * @author Unicorn
 */
public class HttpUtil {
    
    private static CloseableHttpAsyncClient asyncHttpClient = null;

    private static int overtime = 10 * 1000;
    
    private static CloseableHttpClient httpClient = null; 
    
    private HttpUtil() {}
    
    private static void checkAsyncHttpClient() {
        if(asyncHttpClient == null) {
            asyncHttpClient = HttpAsyncClients.createDefault();
            asyncHttpClient.start();
        }
    }
    
    private static void checkHttpClient() {
        if(httpClient == null) {
            httpClient = HttpClients.createDefault();
        }
    }

    public static void setTimeout(int time) {
        overtime = time;
    }

    public static HttpResp uploadFile(String url, Map<String, Object> params) {
        return uploadFile(url, null, params);
    }

    /**
     * 上传文件
     */
    public static HttpResp uploadFile(String url, Map<String,String> headers, Map<String, Object> params) {
        checkHttpClient();
        HttpPost request = new HttpPost(url);
        if(headers != null) {
            for(Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
        RequestConfig requestConfig =
                RequestConfig.custom().setSocketTimeout(overtime).setConnectTimeout(overtime).build();
        request.setConfig(requestConfig);


        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
//        builder.setCharset(Charset.forName(HTTP.UTF_8));        //  设置请求的编码格式
        builder.setMode(HttpMultipartMode.RFC6532);             //  设置浏览器兼容模式

        if(params != null) {
            for(Map.Entry<String,Object> entry : params.entrySet()) {
                if(entry.getValue() instanceof File) {
                    File file = (File) entry.getValue();
                    builder.addBinaryBody(entry.getKey(), file);
                }else if(entry.getValue() instanceof String) {
                    builder.addTextBody(entry.getKey(), (String) entry.getValue());
                }
            }
        }

        HttpEntity reqEntity = builder.build();     // 生成 HTTP POST 实体
        request.setEntity(reqEntity);

        CloseableHttpResponse httpResponse = null;
        HttpResp resp = null;
        try {
            httpResponse = httpClient.execute(request);
            resp = new HttpResp();
            Map<String,String> map = new HashMap<String,String>();
            for(Header header : httpResponse.getAllHeaders()) {
                map.put(header.getName(), header.getValue());
            }
            resp.setHeaders(map);
            resp.setStatusCode(httpResponse.getStatusLine().getStatusCode());
            resp.setBody(IOUtil.toString(httpResponse.getEntity().getContent()));
            httpResponse.close();
        } catch (Exception e) {
            throw new HttpException(e);
        } finally {
            if(httpResponse != null) {
                try {
                    httpResponse.close();
                } catch (IOException e) {
                    throw new HttpException(e);
                }
            }
        }
        return resp;
    }

    public static <T> T getJson(String url, Class<T> clazz) {
        HttpResp resp = get(url);
        if(resp.getStatusCode() == 200) {
            String json = resp.getBody();
            return JSON.parseObject(json, clazz);
        }
        return null;
    }
    
    public static HttpResp get(String url) {
        return get(url, null);
    }

    public static void asyncGet(String url, FutureCallback<HttpResponse> callback) {
        asyncGet(url, null, callback);
    }
    
    public static void asyncPost(String url, Map<String,String> params, FutureCallback<HttpResponse> callback) {
        asyncPost(url, null, params, callback);
    }
    
    public static void asyncPostJson(String url, Object obj, FutureCallback<HttpResponse> callback) {
        asyncPostJson(url, null, obj, callback);
    }
    
    public static void asyncGet(String url, Map<String,String> headers, FutureCallback<HttpResponse> callback) {
        checkAsyncHttpClient();
        HttpGet request = new HttpGet(url);
        if(headers != null) {
            for(Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
        asyncHttpClient.execute(request,callback);
    }
    
    public static void asyncPost(String url, Map<String,String> headers, Map<String,String> params, FutureCallback<HttpResponse> callback) {
        if(params != null) {
            List<NameValuePair> parameters = new ArrayList<NameValuePair>();
            for(Entry<String, String> entry : params.entrySet()) {
                parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            try {
                HttpEntity entity = new UrlEncodedFormEntity(parameters);
                asyncPost(url, headers, entity, callback);
            } catch (UnsupportedEncodingException e) {
                callback.failed(e);
            }
        }
    }
    
    public static void asyncPostJson(String url, Map<String,String> headers, Object obj, FutureCallback<HttpResponse> callback) {
        String json = JSON.toJSONString(obj);
        try {
            HttpEntity entity = new StringEntity(json);
            asyncPost(url, headers, entity, callback);
        } catch (UnsupportedEncodingException e) {
            callback.failed(e);
        }
    }

    public static void asyncPost(String url, Map<String,String> headers, HttpEntity entity, FutureCallback<HttpResponse> callback) {
        checkAsyncHttpClient();
        HttpPost request = new HttpPost(url);
        if(headers != null) {
            for(Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
        request.setEntity(entity);
        asyncHttpClient.execute(request,callback);
    }
    
    public static HttpResp get(String url, Map<String,String> headers) {
        checkHttpClient();
        HttpGet request = new HttpGet(url);
        if(headers != null) {
            for(Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
        RequestConfig requestConfig =
                RequestConfig.custom().setSocketTimeout(overtime).setConnectTimeout(overtime).build();
        request.setConfig(requestConfig);
        CloseableHttpResponse httpResponse = null;
        HttpResp resp = null; 
        try {
            httpResponse = httpClient.execute(request);
            resp = new HttpResp();
            Map<String,String> map = new HashMap<String,String>();
            for(Header header : httpResponse.getAllHeaders()) {
                map.put(header.getName(), header.getValue());
            }
            resp.setHeaders(map);
            resp.setStatusCode(httpResponse.getStatusLine().getStatusCode());
            resp.setBody(IOUtil.toString(httpResponse.getEntity().getContent()));
            httpResponse.close();
        } catch (Exception e) {
            throw new HttpException(e);
        } finally {
            if(httpResponse != null) {
                try {
                    httpResponse.close();
                } catch (IOException e) {
                    throw new HttpException(e);
                }
            }
        }
        return resp;
    }
    

    public static <T> T post(String url, Map<String,String> params, Class<T> clazz) {
        HttpResp resp = post(url,params);
        if(resp.getStatusCode() == 200) {
            String json = resp.getBody();
            return JSON.parseObject(json, clazz);
        }
        return null;
    }
    
    public static <T> T post(Object obj, Class<T> clazz, String url) {
        HttpResp resp = post(obj,url);
        if(resp.getStatusCode() == 200) {
            String json = resp.getBody();
            return JSON.parseObject(json, clazz);
        }
        return null;
    }
    
    public static HttpResp post(String url, Map<String,String> params) {
        return postHeader(url, null, params);
    }
    
    public static HttpResp post(Object obj, String url) {
        return postHeader(null, obj, url);
    }
    
    public static HttpResp postHeader(String url, Map<String,String> headers, Map<String,String> params) {
        List<NameValuePair> parameters = new ArrayList<NameValuePair>();
        if(params != null) {
            for(Entry<String, String> entry : params.entrySet()) {
                parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
        }
        HttpEntity entity = new UrlEncodedFormEntity(parameters, Charset.forName("utf-8"));
        return internalPost(url, headers, entity);
    }
    
    public static HttpResp postHeader(Map<String,String> headers, Object obj, String url) {
        String json = JSON.toJSONString(obj);
        HttpEntity entity = new StringEntity(json, Charset.forName("utf-8"));
        if(headers == null) {
            headers = new HashMap<String,String>();
        }
        headers.put("Content-Type", "application/json; charset=UTF-8");
        return internalPost(url, headers, entity);
    }
    
    public static HttpResp postHeader(String url, Map<String,String> headers, MultipartEntityBuilder builder) {
        HttpEntity entity = builder.build();
        return internalPost(url, headers, entity);
    }

    public static HttpResp postText(String url, String text) {
        return postText(url, null, text);
    }

    public static HttpResp postText(String url, Map<String,String> headers, String text) {
        HttpEntity entity = new StringEntity(text, Charset.forName("utf-8"));
        if(headers == null) {
            headers = new HashMap<String,String>();
        }
        headers.put("Content-Type", "application/json; charset=UTF-8");
        return internalPost(url, headers, entity);
    }
    
    private static HttpResp internalPost(String url, Map<String,String> headers, HttpEntity entity) {
        checkHttpClient();
        HttpPost request = new HttpPost(url);
        if(headers != null) {
            for(Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
        RequestConfig requestConfig =
                RequestConfig.custom().setSocketTimeout(overtime).setConnectTimeout(overtime).build();
        request.setConfig(requestConfig);
       request.setEntity(entity);
        CloseableHttpResponse httpResponse = null;
        HttpResp resp = null; 
        try {
            httpResponse = httpClient.execute(request);
            resp = new HttpResp();
            Map<String,String> map = new HashMap<String,String>();
            for(Header header : httpResponse.getAllHeaders()) {
                map.put(header.getName(), header.getValue());
            }
            resp.setHeaders(map);
            resp.setStatusCode(httpResponse.getStatusLine().getStatusCode());
            resp.setBody(IOUtil.toString(httpResponse.getEntity().getContent()));
            httpResponse.close();
        } catch (Exception e) {
            throw new HttpException(e);
        } finally {
            if(httpResponse != null) {
                try {
                    httpResponse.close();
                } catch (IOException e) {
                    throw new HttpException(e);
                }
            }
        }
        return resp;
    }


    /// 下载

    public static final int cache = 10 * 1024;
    public static final boolean isWindows;
    public static final String splash;
    public static final String root;
    static {
        if (System.getProperty("os.name") != null && System.getProperty("os.name").toLowerCase().contains("windows")) {
            isWindows = true;
            splash = "\\";
            root="D:";
        } else {
            isWindows = false;
            splash = "/";
            root="/search";
        }
    }

    /**
     * 根据url下载文件，文件名从response header头中获取
     * @param url
     * @return
     */
    public static String download(String url) {
        return download(url, null);
    }

    /**
     * 根据url下载文件，保存到filepath中
     * @param url
     * @param filepath
     * @return
     */
    public static String download(String url, String filepath) {
        try {
            HttpClient client = new DefaultHttpClient();
            HttpGet httpget = new HttpGet(url);
            HttpResponse response = client.execute(httpget);
            if(response.getStatusLine().getStatusCode() != 200) {
                return null;
            }
            HttpEntity entity = response.getEntity();
            InputStream is = entity.getContent();
            if (filepath == null)
                filepath = getFilePath(response);
            File file = new File(filepath);
            file.getParentFile().mkdirs();
            FileOutputStream fileout = new FileOutputStream(file);
            /**
             * 根据实际运行效果 设置缓冲区大小
             */
            byte[] buffer=new byte[cache];
            int ch = 0;
            while ((ch = is.read(buffer)) != -1) {
                fileout.write(buffer,0,ch);
            }
            is.close();
            fileout.flush();
            fileout.close();
            return "";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 获取response要下载的文件的默认路径
     * @param response
     * @return
     */
    public static String getFilePath(HttpResponse response) {
        String filepath = root + splash;
        String filename = getFileName(response);

        if (filename != null) {
            filepath += filename;
        } else {
            filepath += getRandomFileName();
        }
        return filepath;
    }
    /**
     * 获取response header中Content-Disposition中的filename值
     * @param response
     * @return
     */
    public static String getFileName(HttpResponse response) {
        Header contentHeader = response.getFirstHeader("Content-Disposition");
        String filename = null;
        if (contentHeader != null) {
            HeaderElement[] values = contentHeader.getElements();
            if (values.length == 1) {
                NameValuePair param = values[0].getParameterByName("filename");
                if (param != null) {
                    try {
                        //filename = new String(param.getValue().toString().getBytes(), "utf-8");
                        //filename=URLDecoder.decode(param.getValue(),"utf-8");
                        filename = param.getValue();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return filename;
    }

    /**
     * 获取随机文件名
     * @return
     */
    public static String getRandomFileName() {
        return String.valueOf(System.currentTimeMillis());
    }
    /**
     * 获取response header
     * @param response
     */
    public static void outHeaders(HttpResponse response) {
        Header[] headers = response.getAllHeaders();
        for (int i = 0; i < headers.length; i++) {
            System.out.println(headers[i]);
        }
    }
}
